from abc import ABC, abstractmethod
import ray
import logging
logger = logging.getLogger(__name__)

class Runnable(ABC):
    """
    An abstract base class representing a runnable task.

    Attributes:
        runnable_name (str): The name of the runnable task.
        run_argc (tuple): Positional arguments for the run method.
        run_argv (dict): Keyword arguments for the run method.
        extra_argv (dict): Additional keyword arguments.
    """

    def __init__(self, runnable_name: str, *argc, **argv):
        self.runnable_name = runnable_name
        if argc:
            self.run_argc = argc
        else:
            self.run_argc = ()

        if argv:
            self.run_argv = argv
        else:
            self.run_argv = {}

        self.extra_argv = {}

    def set_runnable_name(self, runnable_name):
        """
        Set the name of the runnable task.

        Args:
            runnable_name (str): The new name for the runnable task.
        """
        self.runnable_name = runnable_name

    def get_runnable_name(self):
        """
        Get the name of the runnable task.

        Returns:
            str: The name of the runnable task.
        """
        return self.runnable_name

    @abstractmethod
    def _run(self, *argc, **argv):
        """
        Abstract method to be implemented by subclasses for task execution.

        Args:
            *argc: Variable length argument list.
            **argv: Arbitrary keyword arguments.
        """
        pass

    def execute(self):
        """
        Execute the runnable task.

        Returns:
            The result of the _run method execution.
        """
        logger.info("runnable executing %s, with args count: %d, kwargs: %s", self.runnable_name, len(self.run_argc), self.run_argv.keys())
        return self._run(*self.run_argc, **self.run_argv)

    def set_run_param(self, *argc, **argv):
        """
        Set the parameters for the run method.

        Args:
            *argc: Variable length argument list for run method.
            **argv: Arbitrary keyword arguments for run method.
        """
        self.run_argc = argc
        self.run_argv = argv

    def set_extra_param(self, **kwargs):
        """
        Set additional parameters.

        Args:
            **kwargs: Arbitrary keyword arguments.
        """
        self.extra_argv = kwargs

def runnable_task(func):
    """
    Decorator to create a Runnable class from a function.

    Args:
        func (callable): The function to be wrapped as a Runnable.

    Returns:
        type: A ConcreteRunnable class that inherits from Runnable.
    """
    class ConcreteRunnable(Runnable):
        def _run(self, *argc, **argv):
            return func(*argc, **argv)

    return ConcreteRunnable
